package com.ruoyi.accounts.tcp;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.accounts.service.IClientHandler;
import com.ruoyi.accounts.service.registry.ClientHandlerRegistry;

import lombok.var;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Autowired;

@Component
@ServerEndpoint(value = "/ws")
public class WebSocketService implements
        ApplicationContextAware {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);

    // 全局静态变量，保存 ApplicationContext
    private static ApplicationContext applicationContext;


    private Session session;
    public static Map<String, Session> SESSIONS = new ConcurrentHashMap<>();


    private TcpThreadService tcpThreadService;
    
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private GameInfoService gameInfoService;

    // 收到消息
    @OnMessage
    public void onMessage(String message) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        LOGGER.info("[websocket] 收到消息：id={}，message={}", this.session.getId(), message);
        try {
            // 解析命令
            WSCommand command = getCommand(message);
            if (command == null) {
                LOGGER.info("message格式或者类型错误，提前返回");
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(new WSRespone(false, "message格式或者类型错误")));
                return;
            }
            // 登录
            if (command.getType().equals("login")) {
                if (SESSIONS.containsKey(command.getCustomID())) SESSIONS.remove(command.getCustomID());
                SESSIONS.put(command.getCustomID(), session);
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(new WSRespone(true, "登录成功")));
                GameStateService.sendLiveData(session, "take", GameStateService.OCCUPYMACHINE.values());
                return;
            }
            // 获取机器ID,获取前验证一下是否有机器id
            if (command.getMachineID() == null) {
                LOGGER.info("machineID为null，提前返回");
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(
                    new WSRespone(false, "机器ID不能为空")));
                return;
            }
            // 获取硬件号，板子名称代号 emp_equipment
            String hardwareNumber = command.getMachineID();
            // 获取机器类型
            Long machineTypeId = gameInfoService.getMachineTypeFromMachine(hardwareNumber);
            if (machineTypeId == null) {
                LOGGER.info("找不到hardwareNumber为" + hardwareNumber + "的机器类型，提前返回");
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(
                    new WSRespone(false, "找不到hardwareNumber为" + hardwareNumber + "的机器类型")));
                return;
            }
            
            // 获取对应的处理器
            IClientHandler handler = ClientHandlerRegistry.getHandler(machineTypeId);
            if (handler == null) {
                LOGGER.info("找不到类型为" + machineTypeId + "的机器处理器，提前返回");
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(
                    new WSRespone(false, "找不到类型为" + machineTypeId + "的机器处理器")));
                return;
            }
            String type = command.getType();
            if (type == null) {
                LOGGER.info("type类型错误，提前返回");
                session.getBasicRemote().sendText(objectMapper.writeValueAsString(
                    new WSRespone(false, "type类型错误")));
                return;
            }
            // 检查处理器是否支持该命令
            // if (!handler.canHandle(type)) {
            //     LOGGER.info("该机器不支持此命令: " + type);
            //     session.getBasicRemote().sendText(objectMapper.writeValueAsString(
            //         new WSRespone(false, "该机器不支持此命令: " + type)));
            //     return;
            // }
            
            // 处理命令
            handler.handleCommand(command, session, message);
            
        } catch (Exception e) {
            LOGGER.error("处理命令出错: " + e.getMessage(), e);
            session.getBasicRemote().sendText(objectMapper.writeValueAsString(
                new WSRespone(false, "处理命令出错: " + e.getMessage())));
        }
    }

    // 连接打开
    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) throws IOException {
        this.session = session;
        this.tcpThreadService = WebSocketService.applicationContext.getBean(TcpThreadService.class);
        this.stringRedisTemplate = WebSocketService.applicationContext.getBean(StringRedisTemplate.class);
        this.gameInfoService = WebSocketService.applicationContext.getBean(GameInfoService.class);
        LOGGER.info("[websocket] 新的连接：id={}", this.session.getId());
        ObjectMapper objectMapper = new ObjectMapper();
        this.session.getBasicRemote().sendText(objectMapper.writeValueAsString(new WSRespone(true, "连接成功")));
    }

    // 连接关闭
    @OnClose
    public void onClose(CloseReason closeReason) {
        LOGGER.info("[websocket] 连接断开：id={}，reason={}", this.session.getId(), closeReason);
        remove();
    }

    // 连接异常
    @OnError
    public void onError(Throwable throwable) throws IOException {

        LOGGER.info("[websocket] 连接异常：id={}，throwable={}", this.session.getId(), throwable.getMessage());
        remove();
        // 关闭连接。状态码为 UNEXPECTED_CONDITION（意料之外的异常）
        this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
    }

    public void remove() {
        var iterator = SESSIONS.entrySet().iterator();
        while (iterator.hasNext()) {
            var entry = iterator.next();
            if (entry.getValue().equals(this.session)) {
                SESSIONS.remove(entry.getKey());
                break;
            }
        }
    }

    public WSCommand getCommand(String message) {
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            WSCommand command = objectMapper.readValue(message, WSCommand.class);
            return command;
        } catch (IOException ignored) {
            LOGGER.info("message格式错误");
            return null;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        WebSocketService.applicationContext = applicationContext;
    }
}
